/******************************************************************************
* 文件名称：sw_i2c.c
* 内容摘要：软件IIC模块
* 使用说明：见README.md
* 当前版本：V1.00
* 源码仓库：https://gitee.com/thin-wind/Module/
* 作    者：孔佳伟
* 个人主页：https://gitee.com/thin-wind/
* 联系方式: QQ: 445285318
* 修改日期：2022年07月29日
******************************************************************************/
#include "sw_i2c.h"
#include <stdio.h>

static SW_I2C_FUNC sw_i2c_gpio_init = NULL;     /* SDA、SCL引脚设置为输出上拉模式 */
static SW_I2C_FUNC sw_i2c_sda_out_h = NULL;     /* SDA引脚初始化为输出上拉高电平 */
static SW_I2C_FUNC sw_i2c_sda_out_l = NULL;     /* SDA引脚初始化为输出上拉低电平 */
static SW_I2C_FUNC sw_i2c_sda_in = NULL;        /* SDA引脚设置为输入上拉模式 */
static SW_I2C_READ_SDA sw_i2c_read_sda = NULL;  /* 读取SDA引脚电平 */
static SW_I2C_FUNC sw_i2c_sda_h = NULL;         /* SDA引脚输出高电平 */
static SW_I2C_FUNC sw_i2c_sda_l = NULL;         /* SDA引脚输出低电平 */
static SW_I2C_FUNC sw_i2c_scl_h = NULL;         /* SCL引脚输出高电平 */
static SW_I2C_FUNC sw_i2c_scl_l = NULL;         /* SCL引脚输出低电平 */
static SW_I2C_FUNC sw_i2c_delay5us = NULL;      /* 5us延时 */
static SW_I2C_FUNC sw_i2c_delay2us = NULL;      /* 2us延时 */


/*************************************************************************
* 函数名称：static SW_I2C_RETURN sw_i2c_check(void)
* 功能说明：检查是否提供相关接口函数
* 输入参数：无
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
static SW_I2C_RETURN sw_i2c_check(void)
{
    if(  (NULL == sw_i2c_gpio_init) \
      || (NULL == sw_i2c_sda_out_h) \
      || (NULL == sw_i2c_sda_out_l) \
      || (NULL == sw_i2c_sda_in)    \
      || (NULL == sw_i2c_sda_h)     \
      || (NULL == sw_i2c_sda_l)     \
      || (NULL == sw_i2c_scl_h)     \
      || (NULL == sw_i2c_scl_l)     \
      || (NULL == sw_i2c_delay5us)  \
      || (NULL == sw_i2c_delay2us)  \
    )
    {
        return SW_I2C_HOOKS_NULL;
    }
    return SW_I2C_OK;
}

/*************************************************************************
* 函数名称：SW_I2C_RETURN sw_i2c_regist_init(SW_I2C_HOOKS * i2c_hooks)
* 功能说明：注册软件I2C相关接口函数并初始化
* 输入参数：SW_I2C_HOOKS * i2c_hooks       --需提供的相关接口函数集合对象
* 输出参数：无
* 函数返回：操作成功：SW_I2C_OK
*          操作失败：见SW_I2C_RETURN枚举
* 其它说明：无
**************************************************************************/
SW_I2C_RETURN sw_i2c_regist_init(SW_I2C_HOOKS * i2c_hooks)
{
    sw_i2c_gpio_init = i2c_hooks->GPIO_INIT;
    sw_i2c_sda_out_h = i2c_hooks->SDA_OUT_H;
    sw_i2c_sda_out_l = i2c_hooks->SDA_OUT_L;
    sw_i2c_sda_in = i2c_hooks->SDA_IN;
    sw_i2c_read_sda = i2c_hooks->READ_SDA;
    sw_i2c_sda_h = i2c_hooks->SDA_H;
    sw_i2c_sda_l = i2c_hooks->SDA_L;
    sw_i2c_scl_h = i2c_hooks->SCL_H;
    sw_i2c_scl_l = i2c_hooks->SCL_L;
    sw_i2c_delay5us = i2c_hooks->DELAY_5US;
    sw_i2c_delay2us = i2c_hooks->DELAY_2US;

    if(SW_I2C_OK != sw_i2c_check())
    {
        return SW_I2C_HOOKS_NULL;
    }

    sw_i2c_gpio_init();
    sw_i2c_sda_h();
    sw_i2c_scl_h();

    return SW_I2C_OK;
}

/*************************************************************************
* 函数名称：static void sw_i2c_delay(uint32 cnt)
* 功能说明：延时函数
* 输入参数：uint32 cnt      --延时计数
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
void sw_i2c_delay(uint32 cnt)
{
    volatile uint32 delay = cnt;
    while (delay--); 
}

/*************************************************************************
* 函数名称：static void sw_i2c_start(void)
* 功能说明：软件I2C启动
* 输入参数：无
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
static void sw_i2c_start(void)
{
    sw_i2c_sda_h();
    sw_i2c_scl_h();
    sw_i2c_delay5us();
    sw_i2c_sda_l();
    sw_i2c_delay5us();
    sw_i2c_scl_l();
    sw_i2c_delay(1);
}

/*************************************************************************
* 函数名称：static void sw_i2c_stop(void)
* 功能说明：软件I2C停止
* 输入参数：无
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
static void sw_i2c_stop(void)
{
    sw_i2c_scl_l();
    sw_i2c_sda_out_l();
    sw_i2c_sda_l();
    sw_i2c_delay5us();
    sw_i2c_scl_h();
    sw_i2c_delay5us();
    sw_i2c_sda_h();
    sw_i2c_delay(1);
}

/*************************************************************************
* 函数名称：static void sw_i2c_write_byte(unsigned char data)
* 功能说明：软件I2C写一个字节数据
* 输入参数：uint8 data          --写入的数据
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
static void sw_i2c_write_byte(uint8 data)
{
    uint8 temp;
    sw_i2c_sda_out_l();
    sw_i2c_delay2us();
    for(temp = 0x80; temp != 0; temp >>= 1)
    {
        if(0 == (temp & data))
        {
            sw_i2c_sda_l();
        }
        else
        {
            sw_i2c_sda_h();
        }
        sw_i2c_delay2us();
        sw_i2c_scl_h();
        sw_i2c_delay2us();
        sw_i2c_scl_l();
    }
}

/*************************************************************************
* 函数名称：static SW_I2C_RETURN sw_i2c_wait_ack(void)
* 功能说明：等待从机应答
* 输入参数：无
* 输出参数：无
* 函数返回：应答：SW_I2C_ACK
*          无应答：SW_I2C_NCK
* 其它说明：无
**************************************************************************/
static SW_I2C_RETURN sw_i2c_wait_ack(void)
{
    uint32 wait_cnt = 0;
    sw_i2c_sda_in();
    // 这里补充第9个clock时钟
    sw_i2c_delay2us();
    sw_i2c_scl_h();
    sw_i2c_delay2us();
    sw_i2c_scl_l();
    while (SW_I2C_SDA_L != sw_i2c_read_sda())
    {
        wait_cnt++;
        if(wait_cnt > SW_I2C_WAIT_ACK_CNT)
        {
            return SW_I2C_NCK;
        }
        sw_i2c_delay(1);
    }

    return SW_I2C_ACK;
}

/*************************************************************************
* 函数名称：SW_I2C_RETURN sw_i2c_write_bytes(uint8 addr_7bit, uint8 * const tx_buf, uint16 tx_num)
* 功能说明：软件I2C写多个字节数据
* 输入参数：uint8 addr_7bit              --7bit从机地址         
*          uint8 * const tx_buf         --发送的数据区首地址
*          uint16 tx_num                --发送的数据长度
* 输出参数：无
* 函数返回：操作成功：SW_I2C_OK
*          操作失败：见SW_I2C_RETURN枚举
* 其它说明：无
**************************************************************************/
SW_I2C_RETURN sw_i2c_write_bytes(uint8 addr_7bit, uint8 * const tx_buf, uint16 tx_num)
{
    uint16 i = 0;
    uint8 addr_w = 0;
    addr_w = addr_7bit<<1;
    sw_i2c_start();
    sw_i2c_write_byte(addr_w);
    if(SW_I2C_ACK != sw_i2c_wait_ack())
    {
        sw_i2c_stop();
        return SW_I2C_NCK;
    }
    for(i = 0; i < tx_num; i++)
    {
        sw_i2c_write_byte(tx_buf[i]);
        if(SW_I2C_ACK != sw_i2c_wait_ack())
        {
            sw_i2c_stop();
            return SW_I2C_NCK;
        }
    }
    sw_i2c_stop();
    return SW_I2C_OK;
}

/*************************************************************************
* 函数名称：static void sw_i2c_ack(void)
* 功能说明：主机应答
* 输入参数：无
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
static void sw_i2c_ack(void)
{
    sw_i2c_sda_out_l();
    sw_i2c_sda_l();
    sw_i2c_delay2us();
    sw_i2c_scl_h();
    sw_i2c_delay2us();
    sw_i2c_scl_l();
    sw_i2c_delay2us();
}

/*************************************************************************
* 函数名称：static void sw_i2c_nck(void)
* 功能说明：主机无应答
* 输入参数：无
* 输出参数：无
* 函数返回：无
* 其它说明：无
**************************************************************************/
static void sw_i2c_nck(void)
{
    sw_i2c_sda_out_h();
    sw_i2c_sda_h();
    sw_i2c_delay2us();
    sw_i2c_scl_h();
    sw_i2c_delay2us();
    sw_i2c_scl_l();
    sw_i2c_delay2us();
}

/*************************************************************************
* 函数名称：static uint8 sw_i2c_read_byte(uint8 ack)
* 功能说明：软件I2C读一个字节数据
* 输入参数：uint8 ack           --主机是否应答，0：不应答；1：应答
* 输出参数：无
* 函数返回：读出的一个字节数据
* 其它说明：无
**************************************************************************/
static uint8 sw_i2c_read_byte(uint8 ack)
{
    uint8 recv_data = 0, i = 0;
    sw_i2c_sda_in();
    for(i = 0; i < 8; i++)
    {
        sw_i2c_scl_l();
        sw_i2c_delay2us();
        sw_i2c_scl_h();
        recv_data <<= 1;
        if(sw_i2c_read_sda())
        {
            recv_data |= 0x01;
        }
        sw_i2c_delay2us();
    }
    sw_i2c_scl_l();
    if(0 == ack)
    {
        sw_i2c_nck();
    }
    else
    {
        sw_i2c_ack();
    }
    return recv_data;
}

/*************************************************************************
* 函数名称：SW_I2C_RETURN sw_i2c_read_bytes(uint8 addr_7bit, uint8 * rx_buf, uint16 rx_num)
* 功能说明：软件I2C读多个字节数据
* 输入参数：uint8 addr_7bit              --7bit从机地址         
*          uint16 tx_num                --读取的数据长度
* 输出参数：uint8 * rx_buf               --数据读取后存入的数据区首地址
* 函数返回：操作成功：SW_I2C_OK
*          操作失败：见SW_I2C_RETURN枚举
* 其它说明：无
**************************************************************************/
SW_I2C_RETURN sw_i2c_read_bytes(uint8 addr_7bit, uint8 * rx_buf, uint16 rx_num)
{
    uint8 addr_r = 0;
    uint16 i = 0;

    addr_r = (addr_7bit << 1) + 1;
    sw_i2c_start();
    sw_i2c_write_byte(addr_r);
    if(SW_I2C_ACK != sw_i2c_wait_ack())
    {
        sw_i2c_stop();
        return SW_I2C_NCK;
    }
    for(i = 0; i < rx_num; i++)
    {
        if(i == (rx_num - 1))
        {
            rx_buf[i] = sw_i2c_read_byte(0);
        }
        else
        {
            rx_buf[i] = sw_i2c_read_byte(1);
        }
        sw_i2c_delay(SW_I2C_READ_BYTE2BYTE_CNT);
    }
    sw_i2c_stop();
    return SW_I2C_OK;
}

/* end of file */

